#ifndef CLUSNET_NET_UDPSERVER_H
#define CLUSNET_NET_UDPSERVER_H

#include <boost/asio.hpp>
#include <boost/container_hash/hash.hpp>
#include <clusnet/base/Thread.h>
#include "UdpDataPacket.h"
#include "UdpSession.h"
#include "Callbacks.h"
#include <unordered_map>
#include <vector>
#include <boost/array.hpp>

namespace clusnet
{
namespace net
{

class UdpServer final
{
    class EndpointHasher final
    {
        public:
            EndpointHasher() = default;
            ~EndpointHasher() = default;
            EndpointHasher(EndpointHasher const&) = default;
            EndpointHasher& operator= (EndpointHasher const&) = delete;
            std::size_t operator() (boost::asio::ip::udp::endpoint const& e) const
            {
                std::size_t seed = 0;
                boost::hash_combine(seed, e.address().to_string());
                boost::hash_combine(seed, e.port());
                return seed;
            }
    };

    public:
        UdpServer(char const* host, unsigned short port);
        UdpServer(unsigned short port, boost::asio::ip::udp::resolver::protocol_type const& proto = boost::asio::ip::udp::v4());
        UdpServer(boost::asio::ip::udp::endpoint const& endpoint);
        ~UdpServer();
        UdpServer(UdpServer const&) = delete;
        UdpServer& operator= (UdpServer const&) = delete;

    public:
        void start(size_t numThreads = 1);
        void stop();
        void setMessageCallback(UdpMessageCallback const& cb) { messageCallback_ = cb; }
        void setWriteCompleteCallback(UdpWriteCompleteCallback const& cb) { writeCallback_ = cb; }

    private:
        void safeStartReceive();
        void handleReceive(boost::system::error_code const& ec, std::size_t len);

    private:
        boost::asio::io_context             io_;
        boost::asio::io_context::strand     strand_; 
        boost::asio::ip::udp::endpoint      localEndpoint_;
        boost::asio::ip::udp::endpoint      remoteEndpoint_;
        boost::asio::ip::udp::socket        socket_;
        std::vector<std::unique_ptr<clusnet::base::Thread>>     threads_;
        std::unordered_map<boost::asio::ip::udp::endpoint, UdpSessionPtr, EndpointHasher>   sessions_;
        bool                                stop_;
        boost::array<char, UdpDataPacket::maxLength()>           recvBuffer_;
        
        UdpMessageCallback                  messageCallback_;
        UdpWriteCompleteCallback            writeCallback_;
};
}
}
#endif 
